💰 Custo de Consultas

Objetivo: dada uma árvore de execução (σ, ⨝, π) e as estatísticas do catálogo, achar o custo mínimo total em acessos a bloco, justificando a escolha de cada operação pelo custo de todas as alternativas.


🧭 Comece aqui

A ideia em uma frase. O banco consegue responder a mesma consulta de vários jeitos: ler a tabela inteira, usar um índice, juntar duas tabelas de formas diferentes. A parte lenta é ler do disco, então o custo de cada jeito é medido em quantos blocos ele lê. O banco calcula o custo de todos os jeitos e escolhe o mais barato, e a prova pede que você faça a mesma conta.

Uma analogia. Você precisa achar as fichas dos alunos com nota 10 numa pilha com centenas de fichas. Jeito 1: olhar todas, o que sempre funciona. Jeito 2: usar uma lista separada, organizada por nota, que diz onde está cada ficha (um índice). O jeito 2 compensa se forem poucos alunos. Se metade da turma tirou 10, você acaba abrindo quase todas as fichas de qualquer jeito, e ainda pagou para ler a lista. Toda a matéria é essa comparação, com números.

O que a questão pede. Uma árvore com as operações da consulta. Para cada operação: liste todos os métodos possíveis, calcule o custo de cada um (ou diga por que não se aplica), escolha o menor e, no fim, some.

Palavras que vão aparecer:

Onde este arquivo se encaixa. É o 3º de 3. Ele usa o número de blocos de cada tabela ("Banco de Dados: Organização de Arquivos") e os níveis e folhas dos índices ("Banco de Dados: Indexação e Árvore B+").

Se você está perdido, leia nesta ordem: este "Comece aqui", depois o Exemplo resolvido (seção 11) acompanhando a Receita (seção 10). As seções 3 a 9 explicam cada conta do exemplo: volte a elas quando um número não fizer sentido.


⚙️ 1. Como o SGBD processa uma consulta

flowchart LR A["SQL"] --> B["Interpretador<br/>léxico, sintático, semântico<br/>(consulta o catálogo)"] B --> C["Árvore de consulta<br/>canônica"] C --> D["Gerador de código<br/>(escolhe os algoritmos)"] D --> E["Executor"] --> F["Resultado"]
Materialização Pipelining
Resultado intermediário Gravado em disco Passa por um buffer em memória direto para o operador de cima
Custo + escrita e releitura Nenhum acesso extra
Quando Operando não cabe na memória Padrão. Operadores por tupla (σ, π) fluem; operadores que precisam da tabela inteira (ordenação) bloqueiam o pipeline.

O pipelining usa iteradores: Open() prepara a operação, GetNext() devolve a próxima tupla (ou NotFound) e Close() libera os buffers. Consequência na prova: a projeção no topo da árvore custa 0 com pipelining.


📖 2. Notação

Não decore esta tabela: cada símbolo aparece explicado na conta em que é usado. Quatro deles aparecem em quase toda conta:

A tabela completa abaixo é de consulta. Os valores ao lado são os do exemplo resolvido (seção 11).

Símbolo Significado Exemplo
rr Número de tuplas da tabela empregado: 1 000
RR Tamanho de uma tupla em bytes: soma de todas as colunas 312 B
BB Tamanho da página (bloco) 1 024 B
BfrBfr Tuplas por bloco: ⌊B/R⌋\lfloor B/R \rfloor 3
bb Blocos da tabela: ⌈r/Bfr⌉\lceil r/Bfr \rceil 334
dd Valores distintos de um atributo salario: 5
ss Quantas tuplas a condição devolve 200
s/rs/r Seletividade: a fração das tuplas que passa 0,20 (200 de 1 000)
xx Níveis do índice, da raiz até a folha 2
bleafb_{leaf} Blocos-folha do índice 30
bbufferb_{buffer} Blocos de buffer para junção e ordenação 5
nrn_r Runs iniciais da ordenação externa: ⌈b/bbuffer⌉\lceil b/b_{buffer} \rceil 5
gmg_m Grau de merge: bbuffer−1b_{buffer} - 1 4
jsjs Seletividade da junção: 1/max⁡(dA,dB)1/\max(d_A, d_B) 1/1 000

🧱 3. Passo 1: tamanho das tabelas

R=∑colunasBfr=⌊BR⌋b=⌈rBfr⌉R = \sum \text{colunas} \qquad Bfr = \left\lfloor \frac{B}{R} \right\rfloor \qquad b = \left\lceil \frac{r}{Bfr} \right\rceil


🎯 4. Passo 2: seletividade

A seletividade é a fração das tuplas que satisfaz a condição. Se 20% de 1 000 tuplas passam, a seleção devolve s=200s = 200 tuplas. Quanto menor, mais um índice compensa: o índice secundário paga cerca de 1 acesso por tupla encontrada. Com 200 de 1 000 (20%) ele ainda ganha da leitura da tabela inteira (207 < 334). Com 350 de 1 000 (35%), já perde (362 > 334).

Condição Estimativa Exemplo
Igualdade A = v s=r/ds = r/d (supõe os valores espalhados por igual) salario = 4000: 1000/5 = 200 (20%)
Igualdade em chave (único) s=1s = 1 id = 7
Faixa, com histograma Soma das faixas cobertas dt_nasc ≥ 1980: 200 + 150 = 350 (35%)
A AND B sA∧B=r⋅sAr⋅sBrs_{A \land B} = r \cdot \frac{s_A}{r} \cdot \frac{s_B}{r} 1000 × 0,20 × 0,35 = 70 (7%)
A OR B sA∨B=sA+sB−sA∧Bs_{A \lor B} = s_A + s_B - s_{A \land B} 200 + 350 − 70 = 480 (48%)

🔍 5. Passo 3: algoritmos de seleção

Método Custo (acessos) Quando vale Exemplo
Busca sequencial bb Sempre 334
Busca binária ⌈log⁡2b⌉+⌈s/Bfr⌉−1\lceil \log_2 b \rceil + \lceil s/Bfr \rceil - 1 Arquivo ordenado pelo atributo Não se aplica
Índice primário / clustering x+⌈s/Bfr⌉x + \lceil s/Bfr \rceil Arquivo ordenado pelo atributo e índice nele Não se aplica
Índice secundário (B+) x+⌈bleaf⋅s/r⌉−1+sx + \lceil b_{leaf} \cdot s/r \rceil - 1 + s Índice num atributo que não ordena o arquivo —
Conjuntiva, índice simples Custo do índice de uma condição, com o ss dela; a outra é testada em memória Pelo menos uma condição indexada salario: 2 + 6 − 1 + 200 = 207; dt_nasc: 2 + 11 − 1 + 350 = 362
Conjuntiva, índice composto Custo do índice (A,B)(A, B) com sA∧Bs_{A \land B} Índice composto nos dois atributos Não se aplica
Conjuntiva, índices múltiplos cA∗+cB∗+sA∧Bc^{*}_{A} + c^{*}_{B} + s_{A \land B} Índice em cada condição 7 + 12 + 70 = 89 ✅
Disjuntiva (OR) indexada cA∗+cB∗+sA∨Bc^{*}_{A} + c^{*}_{B} + s_{A \lor B} Índice em todas as condições; senão, sequencial 7 + 12 + 480 = 499

A fórmula do índice secundário, termo a termo:

Csec=x⏟desce ateˊ a primeira folha+⌈bleaf⋅sr⌉⏟folhas com as s entradas− 1⏟primeira folha jaˊ contada+s⏟1 bloco por tuplaC_{sec} = \underbrace{x}_{\text{desce até a primeira folha}} + \underbrace{\left\lceil b_{leaf} \cdot \frac{s}{r} \right\rceil}_{\text{folhas com as } s \text{ entradas}} \underbrace{- \,1}_{\text{primeira folha já contada}} + \underbrace{s}_{\text{1 bloco por tupla}}

c∗c^{*} é o custo do índice sem o +s+ s: c∗=x+⌈bleaf⋅s/r⌉−1c^{*} = x + \lceil b_{leaf} \cdot s/r \rceil - 1. Nos índices múltiplos, cada índice entrega só uma lista de rowIds. As listas são cruzadas em memória (interseção no AND, união no OR), e só as tuplas que sobram são buscadas no arquivo. No exemplo: csal∗=2+6−1=7c^{*}_{sal} = 2 + 6 - 1 = 7 e cdt∗=2+11−1=12c^{*}_{dt} = 2 + 11 - 1 = 12.


📦 6. Passo 4: tamanho do resultado intermediário

bsel=⌈sBfr⌉=⌈703⌉=24 blocosb_{sel} = \left\lceil \frac{s}{Bfr} \right\rceil = \left\lceil \frac{70}{3} \right\rceil = 24 \text{ blocos}


🔗 7. Passo 5: algoritmos de junção

Método Custo Requisito Exemplo
Nested loops bouter+⌈bouterbbuffer−2⌉⋅binnerb_{outer} + \left\lceil \frac{b_{outer}}{b_{buffer} - 2} \right\rceil \cdot b_{inner} Sempre. Calcule as duas ordens. seleção externa: 24 + 8 × 5 = 64; departamento externa: 5 + 2 × 24 = 53 ✅
Index-based bouter+router⋅cıˊndiceb_{outer} + r_{outer} \cdot c_{\text{índice}} Índice no atributo de junção da relação interna, que precisa ser tabela base índice em id_gerente: 24 + 70 × 3 = 234; departamento externa: não se aplica
Sort-merge bR+bSb_R + b_S + ordenações Ordena as duas pelo atributo de junção (a ordenação some se já estiverem ordenadas) 144 + 5 + (24 + 5) = 178
Hash 2 bR+bS2\,b_R + b_S + nested loops de cada bucket RR é a relação particionada RR = seleção: 53 + buckets; RR = departamento: 34 + buckets ⚠️

Cord=2(b+b⋅⌈log⁡gmnr⌉)nr=⌈bbbuffer⌉gm=bbuffer−1C_{ord} = 2 \left( b + b \cdot \lceil \log_{g_m} n_r \rceil \right) \qquad n_r = \left\lceil \frac{b}{b_{buffer}} \right\rceil \qquad g_m = b_{buffer} - 1


📤 8. Passo 6: projeção e agregação


🔢 9. Arredondamentos

Grandeza Arredonda Exemplo
BfrBfr Para baixo ⌊1024/312⌋=3\lfloor 1024/312 \rfloor = 3
bb, bselb_{sel} Para cima ⌈70/3⌉=24\lceil 70/3 \rceil = 24
log (passadas, níveis) Para cima ⌈log⁡45⌉=2\lceil \log_4 5 \rceil = 2
Fração de bloco (bleaf⋅s/rb_{leaf} \cdot s/r, pedaços do nested loops) Para cima, no mínimo 1 ⌈30×0,35⌉=11\lceil 30 \times 0{,}35 \rceil = 11; ⌈5/50⌉=1\lceil 5/50 \rceil = 1
Número de tuplas fracionário Para cima (diga) 3,5 → 4

✅ 10. Receita na prova

  1. RR, BfrBfr e bb de cada tabela, somando todas as colunas.
  2. ss de cada condição e da combinação (AND por independência, OR por inclusão-exclusão).
  3. Seleção: uma tabela com todos os métodos, cada um com custo ou "não se aplica (motivo)". Escolha o menor e compare explicitamente com a busca sequencial.
  4. bb do resultado da seleção.
  5. Junção: todos os métodos, nas duas ordens, com "não se aplica (motivo)" onde couber. Escolha o menor.
  6. Projeção/agregação: diga se usou pipelining.
  7. Total = soma dos mínimos, em acessos a bloco.
  8. Escreva as suposições (hash, pipelining, arredondamentos).

📝 11. Exemplo resolvido completo

Enunciado: apresente o custo mínimo total da árvore de execução abaixo, justificando o custo mínimo de cada operação pelos custos das demais opções. Buffer para junção e ordenação: 5 blocos. Página: 1 024 B.

               π emp.nome, dep.nome
                        │
            ⨝ emp.id = dep.id_gerente
              ┌─────────┴─────────┐
              │                   │
   σ salario = 4000.00       departamento
   AND dt_nasc ≥ '01-01-1980'
              │
          empregado

Estatísticas das tabelas:

Tabela Linhas
empregado 1 000
departamento 50

Estatísticas das colunas:

Tabela Coluna Distintos Bytes
empregado id 1 000 8
empregado nome 850 80
empregado salario 5 16
empregado dt_nasc 700 8
empregado endereco 990 200
departamento id 50 8
departamento nome 50 80
departamento id_gerente 50 8

Histograma:

Faixa de empregado.dt_nasc Tuplas
< 01-01-1970 250
≥ 01-01-1970 e < 01-01-1980 400
≥ 01-01-1980 e < 01-01-1990 200
≥ 01-01-1990 150

Índices:

Índice Tabela Atributo Tipo Níveis Blocos-folha
emp_sal_ix empregado salario secundário 2 30
emp_dt_ix empregado dt_nasc secundário 2 30
dept_id_ix departamento id secundário 2 5
dept_idg_ix departamento id_gerente secundário 2 5

Passo 1. empregado: R=312R = 312, Bfr=3Bfr = 3, b=334b = 334. departamento: R=96R = 96, Bfr=10Bfr = 10, b=5b = 5.

Passo 2. salario = 4000: s1=1000/5=200s_1 = 1000/5 = 200 (20%). dt_nasc ≥ 1980: s2=200+150=350s_2 = 200 + 150 = 350 (35%, pelo histograma). AND por independência: s=1000×0,20×0,35=70s = 1000 \times 0{,}20 \times 0{,}35 = 70 (7%).

Passo 3. Seleção, todas as opções.

Método Conta Custo
Sequencial bb 334
Busca binária Arquivo não ordenado por salario nem por dt_nasc Não se aplica
Índice primário/clustering Não existe Não se aplica
Conjuntiva simples, índice de salario 2 + 30 × 0,2 − 1 + 200 207
Conjuntiva simples, índice de dt_nasc 2 + ⌈30 × 0,35⌉ − 1 + 350 362 (pior que a sequencial)
Conjuntiva composta Não há índice (salario, dt_nasc) Não se aplica
Conjuntiva múltipla (2 + 6 − 1) + (2 + 11 − 1) + 70 89 ✅

Passo 4. bsel=⌈70/3⌉=24b_{sel} = \lceil 70/3 \rceil = 24 blocos.

Passo 5. Junção, todas as opções (buffer 5, pedaços de 3 blocos).

Método Conta Custo
Nested loops, seleção externa 24 + ⌈24/3⌉ × 5 64
Nested loops, departamento externa 5 + ⌈5/3⌉ × 24 53 ✅
Index-based, dept_idg_ix na interna 24 + 70 × (2 + ⌈5 × 1/50⌉ − 1 + 1) 234
Index-based, departamento externa O resultado intermediário não tem índice Não se aplica
Sort-merge Ordenar a seleção: 2 × (24 + 24 × ⌈log₄ 5⌉) = 144; ordenar departamento em memória: 5; merge: 24 + 5 178
Hash, RR = seleção 2 × 24 + 5 + buckets 53 + buckets
Hash, RR = departamento 2 × 5 + 24 + buckets 34 + buckets ⚠️

Passo 6. A projeção usa pipelining sobre as tuplas que saem da junção: 0.

Resposta.

flowchart TB P["π emp.nome, dep.nome<br/><i>pipelining: 0</i>"] J["⨝ emp.id = dep.id_gerente<br/><i>nested loops,<br/>departamento externa: 53</i>"] S["σ salario = 4000<br/>AND dt_nasc ≥ 1980<br/><i>conjuntiva múltipla: 89</i>"] E[("empregado<br/>b = 334")] D[("departamento<br/>b = 5")] P --- J J --- S J --- D S --- E

custo mıˊnimo total=89+53+0=142 acessos a bloco\text{custo mínimo total} = 89 + 53 + 0 = \mathbf{142} \text{ acessos a bloco}


⚠️ 12. Pegadinhas


🔁 13. Variações que podem cair

Se o enunciado tiver… Faça
Condição com OR sA∨Bs_{A \lor B} por inclusão-exclusão. Disjuntiva indexada só se todas as condições têm índice. No exemplo daria 7 + 12 + 480 = 499 > 334, e a sequencial ganha.
Igualdade em chave com índice s=1s = 1. Índice secundário: x+⌈bleaf/r⌉−1+1=x+1x + \lceil b_{leaf}/r \rceil - 1 + 1 = x + 1. Primário: x+⌈1/Bfr⌉=x+1x + \lceil 1/Bfr \rceil = x + 1.
Arquivo ordenado pelo atributo da condição Entram a busca binária e o índice primário/clustering.
Índice composto (A,B)(A, B) Conjuntiva composta com sA∧Bs_{A \land B}.
Índice nos dois lados da junção Index-based nas duas ordens (só tabela base como interna).
Tabela já ordenada pelo atributo de junção Sort-merge sem o custo de ordenar essa tabela.
Buffer maior Recalcule os pedaços do nested loops (bbuffer−2b_{buffer} - 2), nrn_r e gmg_m.
Materialização em vez de pipelining Some a escrita do resultado intermediário (e a releitura).
DISTINCT ou GROUP BY no topo Some ordenação (ou hash) + bb.